package com.zebra.gateway.protocol;

import com.zebra.gateway.GwConfigInfoManager;
import com.zebra.remoting.config.BasisCommandCode;
import com.zebra.remoting.config.ConstantValue;
import com.zebra.remoting.protocol.CommandProfile;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.util.List;

/**
 * 游戏服务器〈消息解码器〉
 *
 * @author King.Song
 * @date 2019/8/9 00:05
 */
public class GatewayDecoder extends MessageToMessageDecoder<ByteBuf> {

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(GatewayDecoder.class);

    public GatewayDecoder() {
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        if (in == null) {
            return;
        }
        if (in.readableBytes() < ConstantValue.HEADER_SIZE) {
            logger.error("Decoder Channel close By: Decoder ByteBuf too small {}", ctx.channel().toString());
            ctx.close().sync();
            return;
        }

        if (in.readableBytes() > ConstantValue.MESSAGE_MAX_FRAME_LENGTH) {
            logger.error("Decoder Channel close By: Decoder ByteBuf too big {}", ctx.channel().toString());
            ctx.close().sync();
            return;
        }

        GatewayCommand message = GatewayCommand.builder()
                .flag(in.readShort())
                .length(in.readShort())

                .cmd(in.readShort())
                .token(in.readLong())  // 前端 必须传 同时服务器必须回正确的
                .userId(in.readLong()) // 只有登录时候 传入; 其他接口不用传 //TODO 返回客户端UserId 必须清空
                .state(in.readShort()) // 服务器必须返回
                .gameId(in.readShort()) //前端 进入游戏必须传【其他时候不能传】
                .id(in.readInt())
                .reserve(in.readShort())  //该命值 用于TMS 向 GW 推送消息；GW 要校验CMD 合法性; 通过该值 判断是否 需要校验
                .build();

//        if (flag != (short) (cmd ^ len ^ 1616) || len < 16) //len = cmd+bytes.length
        if (message.getFlag() != (short) (message.getCmd() ^ message.getLength() ^ 1616) || message.getLength() < 28) //len = cmd+bytes.length
        {
            logger.error("Decoder Channel close By ByteBuf parse flag: Channel{}, cmd:{}; length：{}", ctx.channel().toString(), message.getCmd(), message.getLength());
            ctx.close().sync();
            return;
        }

        int length = message.getLength() - 28;
        int realLength = in.readableBytes();
        if (realLength < length) {
            logger.error("Decoder Channel close By ByteBuf parse length: Channel{}, cmd:{}; length：{}, len{}", ctx.channel().toString(), message.getCmd(), length, realLength);
            ctx.close().sync();
            return;
        }

        ByteBuf buf = in.readBytes(length);
        message.setMsgBuf(buf);

        CommandProfile commandModel = BasisCommandCode.PUSHCMD.cmd() == message.getReserve() ?
                GwConfigInfoManager.getInstance().getCmdByCmd(BasisCommandCode.PUSHCMD.cmd())
                : GwConfigInfoManager.getInstance().getCmdByCmd(message.getCmd());
        if (null == commandModel) {
            message.release();
            logger.error("Decoder Channel close By: Decoder Server could not execute the command! {} {}", ctx.channel().toString(), message.toString());
            ctx.close().sync();
            return;
        }
        message.setCommandProfile(commandModel);
        out.add(message);
    }



}